package ch.ethz.fcl.metrobuzz.algorithm.database;

import java.util.ArrayList;
import java.util.List;

import ch.ethz.fcl.metrobuzz.algorithm.list.ListOperation;
import ch.ethz.fcl.metrobuzz.data.long_term_trajectory.TrajectoryGraph;
import ch.ethz.fcl.metrobuzz.data.long_term_trajectory.node.LTTNode;
import ch.ethz.fcl.metrobuzz.data.long_term_trajectory.trajectory.FrequentLTT;

public class LongTermTransactionList {
	private TrajectoryGraph trans;
	private LTTNode selectedNode;
	private FrequentLTT fis;

	private boolean debug = false;

	List<LongTermTransaction> lttList;

	public LongTermTransactionList(TrajectoryGraph transaction,
			LTTNode selectedNode, FrequentLTT fis) {
		this.trans = transaction;
		this.selectedNode = selectedNode;
		this.fis = fis;
	}

	public void update(List<AssociationNodeTransactionList> freqTransList) {
		long startTime = System.currentTimeMillis();

		List<AssociationNodeTransactionList> combinedFreq = combineTransList(freqTransList);
		lttList = new ArrayList<LongTermTransaction>();

		List<AssociationNodeTransactionList> selectedNodeTrans = fis
				.getSelectedNodeTrans();

		for (int i = 0; i < combinedFreq.size(); i++) {
			AssociationNodeTransactionList transList = combinedFreq.get(i);
			int selectedNodeIndex = transList.getNodeList().indexOf(
					selectedNode);
			List<Integer> allTransIndices = new ArrayList<Integer>();
			if (selectedNodeIndex != 0) {
				AssociationNodeTransactionList preTransList = new AssociationNodeTransactionList();
				preTransList.addNode(transList.getNodeList().get(
						selectedNodeIndex - 1));
				preTransList.addNode(transList.getNodeList().get(
						selectedNodeIndex));
				for (AssociationNodeTransactionList nodeTrans : selectedNodeTrans)
					if (nodeTrans.equals(preTransList))
						allTransIndices.addAll(nodeTrans.getTransIndexList());
			}

			else if (selectedNodeIndex != transList.getNodeList().size() - 1) {
				AssociationNodeTransactionList postTransList = new AssociationNodeTransactionList();
				postTransList.addNode(transList.getNodeList().get(
						selectedNodeIndex));
				postTransList.addNode(transList.getNodeList().get(
						selectedNodeIndex + 1));
				for (AssociationNodeTransactionList nodeTrans : selectedNodeTrans)
					if (nodeTrans.equals(postTransList)) {
						if (selectedNodeIndex != 0) {
							List<Integer> sameIndices = ListOperation
									.intersection(allTransIndices,
											nodeTrans.getTransIndexList());
							allTransIndices = new ArrayList<Integer>();
							allTransIndices.addAll(sameIndices);
						} else {
							allTransIndices.addAll(nodeTrans
									.getTransIndexList());
						}
					}
			}

			LongTermTransaction ltt = new LongTermTransaction(trans,
					selectedNode, transList, allTransIndices);
			lttList.add(ltt);
		}

		if (debug) {
			System.out.println("Long term transactions finished in "
					+ (System.currentTimeMillis() - startTime) / 1000.0f);
		}
	}

	private List<AssociationNodeTransactionList> combineTransList(
			List<AssociationNodeTransactionList> transList) {
		List<AssociationNodeTransactionList> combinedList = new ArrayList<AssociationNodeTransactionList>(
				transList);

		List<AssociationNodeTransactionList> removedList = null;
		List<AssociationNodeTransactionList> newCombinedList = null;
		while (removedList == null || removedList.size() != 0) {
			removedList = new ArrayList<AssociationNodeTransactionList>();
			newCombinedList = new ArrayList<AssociationNodeTransactionList>();

			for (int i = 0; i < combinedList.size(); i++) {
				boolean combined = false;
				for (int j = i + 1; j < combinedList.size(); j++) {
					if (combinedList.get(i).contains(combinedList.get(j))) {
						removedList.add(combinedList.get(j));
						combined = true;
						break;
					} else if (combinedList.get(j)
							.contains(combinedList.get(i))) {
						removedList.add(combinedList.get(i));
						combined = true;
						break;
					}
					AssociationNodeTransactionList combine = combinedList
							.get(i).combines(combinedList.get(j));
					if (combine != null) {
						removedList.add(combinedList.get(i));
						removedList.add(combinedList.get(j));
						newCombinedList.add(combine);
						combined = true;
						if (debug) {
							System.out.println(i + " and " + j
									+ " are combined");
							System.out.println("Input 1: "
									+ combinedList.get(i).toString());
							System.out.println("Input 2: "
									+ combinedList.get(j).toString());
							System.out
									.println("Output : " + combine.toString());
						}
						break;
					}
				}
				if (combined)
					break;
			}
			combinedList.addAll(newCombinedList);
			combinedList.removeAll(removedList);
		}

		if (debug) {
			System.out.println("After combination, get " + combinedList.size()
					+ " combined list, they are");
			for (AssociationNodeTransactionList antl : combinedList)
				System.out.println(antl.toString());
		}

		return combinedList;
	}

	public void render() {
		if (lttList != null)
			for (int i = 0; i < lttList.size(); i++) {
//				if (i != 1)
//					continue;
				LongTermTransaction ltt = lttList.get(i);
				ltt.render();
			}
	}
}
